package openadmin.dao.operation;

import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;

import openadmin.dao.Base;
import openadmin.dao.exception.*;
import openadmin.model.control.User;
import openadmin.util.configuration.TypeEnvironment;

/**
 * <desc>DaoJpaHibernate implement the interface DaoOperationFacade</desc>
 * <responsibility>Implement the operations of database for framework JPA - Hibernate</responsibility>
 * <coperation>All classes that need to work with database</coperation>
 * @version  0.1
 * Create  18-03-2009
 * Author Alfred Oliver
*/
public class DaoJpaHibernate implements DaoOperationFacade {
		
	/**Field that contain one list of Base objects */
	private List <Base> listObjects = new ArrayList <Base>();	
	
	/**Field used by the application to obtain an application-managed entity manager.*/
	private EntityManagerFactory factory = null;		
	
	/**Field that contain the interface used to interact with the persistence context.*/
	private EntityManager em; 	
	
	/** Field that contain the result to operation*/
	private boolean resultOperation = false;
	
	/** Field that contain the registered User*/
	private User user;
	
	/** Field that contain the log to record the operations performed*/
	private LogOperationFacade log;
	
	private List<String> listLog = new ArrayList<String>();
	
	/**Field that contain the working environment*/
	private TypeEnvironment environment = TypeEnvironment.SWING;
	
	/** Field that contain the working language*/
	private ResourceBundle language = ResourceBundle.getBundle("messagesDao_ca");	
		
	
	/**
	  * Constructor of class DaoJpaHibernate.
	  * @throws DataException. 
	  * @param PUser. Registered User.  	 
	  * @param 	pDataBase. Attribute name of node persistence-unit (file persistence.xml)
	  * @param pLog. To record the operations performed. 
	  */
	public DaoJpaHibernate(User pUser, String pDataBase, LogOperationFacade pLog) throws DataException{
		
		if (pUser != null) this.setUser(pUser);
		
		if (pLog != null){			
			log = pLog;
			listLog.clear();
			listLog.add(null);		//Object
			listLog.add(null);		//idObject
			listLog.add(null);		//Action
			listLog.add(log.language("CONNECTION") + " " +pDataBase);	//Description
			log.recordLog(listLog);
		}
		
		connection(pDataBase);
		
	}
	
	
	/**
	  * Procedure that connect to database
	  * {Pre: There isn't an open connection to the database}.
	  * {Post: has connect to database}.
	  * @throws DataException if there isn't a connection to the database.
	  * @param 	pDataBase. Attribute name of node persistence-unit (file persistence.xml)  
	  */
	private void connection(String pDataBase)throws DataException {		
						
		try {
			
			factory = Persistence.createEntityManagerFactory(pDataBase, System.getProperties());
			em = factory.createEntityManager();											
			
		}catch(Exception ex) {
			
			ex.printStackTrace();
			
			throw new DataException (ErrorDao.CONNETION, ex.getMessage(),
					ex.getClass().getName(),
					language,
					environment);
		}
	}
	
	/**
	 * Persist object. 
	 * {Pre: The object no exist}.
	 * {Post: The object is persist is not exist. 
	 */
	public void persistObject(Base obj) throws DataException{
		
		resultOperation = false;		
		log.activateLog(obj.getDebugLog());
		log.activatehistoric(false);
		
		
		try{
			if (em.isOpen()){				
				em.persist(obj);
				resultOperation = true;				
				listLog.clear();
				listLog.add(log.language(obj.getClass().getSimpleName()));
				listLog.add(obj.getId().toString());
				listLog.add(log.language("PERSIST"));
				listLog.add(obj.toString());
				log.recordLog(listLog);
			}
		}catch(Exception ex) {
			
			ex.printStackTrace();
			
			resultOperation = false;
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add("0");
			listLog.add(log.language("ERROR_PERSIST"));
			listLog.add(obj.toString()+"; " + log.language("ERROR_CLASS") + ex.getClass().getName());
			log.recordLog(listLog);
			
			
			
			throw new DataException(ErrorDao.PERSIST, 
					language.getString("ERROR_PERSIST") + "\n"  + 
					language.getString(obj.getClass().getSimpleName()) + " " + obj.getDescription(),					
					ex.getClass().getName(),
					language,
					environment);					
		}				
		
	}	
	
	/**
	 * Update object. 
	 * {Pre: The object exist}.
	 * {Post: The object is update if exist. 
	 */
	public void updateObject (Base objOriginal, Base obj) throws DataException{
		
		resultOperation = false;
		log.activateLog(obj.getDebugLog());
		log.activatehistoric(obj.getHistoricLog());				
		
		try{
			if (em.isOpen() && obj != null){										
							
				em.merge(obj);
				resultOperation = true;
				listLog.clear();
				listLog.add(log.language(obj.getClass().getSimpleName()));
				listLog.add(obj.getId().toString());
				listLog.add(log.language("UPDATE"));
				listLog.add(obj.toString());				
				log.detailLog(objOriginal, obj);
				log.recordLog(listLog);
			}
		
		}catch(Exception ex) {
			
			ex.printStackTrace();
			
			resultOperation = false;
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add(obj.getId().toString());
			listLog.add(log.language("ERROR_UPDATE"));
			listLog.add(obj.toString()+"; " + log.language("ERROR_CLASS") + ex.getClass().getName());
			log.recordLog(listLog);			
				
			throw new DataException(ErrorDao.UPDATE, 
					language.getString("ERROR_UPDATE") + "\n"  + 
					language.getString(obj.getClass().getSimpleName()) + " " + obj.getDescription(), 
					ex.getClass().getName(),
					language,
					environment);			
		}
		
	}
	
	/**
	 * Remove object. 
	 * {Pre: The object exist}.
	 * {Post: The object is remove. 
	 */
	public void removeObject (Base obj) throws DataException{
		
		resultOperation = false;
		log.activateLog(obj.getDebugLog());
		log.activatehistoric(false);
		
		try{			
			if (em.isOpen() && (findObjectPK(obj)) != null){
				
				em.remove(obj);				
				
				resultOperation = true;
				
				listLog.clear();
				listLog.add(log.language(obj.getClass().getSimpleName()));
				listLog.add(obj.getId().toString());
				listLog.add(log.language("REMOVE"));
				listLog.add(obj.toString());
				log.recordLog(listLog);				
			}
			
		} catch(Exception ex) {
			
			ex.printStackTrace();
			
			resultOperation = false;
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add(obj.getId().toString());
			listLog.add(log.language("ERROR_REMOVE"));
			listLog.add(obj.toString()+"; " + log.language("ERROR_CLASS") + ex.getClass().getName());
			log.recordLog(listLog);				
						
			throw new DataException(ErrorDao.REMOVE, 
					language.getString("ERROR_REMOVE") + "\n"  + 
					language.getString(obj.getClass().getSimpleName()) + " " + obj.getDescription(), 
					ex.getClass().getName(),
					language,
					environment);					
		}		
	}	
	
	/**
	 * Find object of type Base by primary key. 
	 * {Pre: Primary key of object}.
	 * {Post: return object if exist}. 
	 */
	public Base findObjectPK (Base obj) throws DataException{
		
		resultOperation = false;
		
		Base new_obj = null;
		
		try{
			if (em.isOpen()){
																							
				new_obj = em.find(obj.getClass(), obj.getId());												
				
				resultOperation = true;
				
				listLog.clear();
				listLog.add(log.language(obj.getClass().getSimpleName()));
				listLog.add(obj.getId().toString());
				listLog.add(log.language("FIND_PK"));
				listLog.add(obj.toString());
				log.recordLog(listLog);						
			
			}
			
		}catch(Exception ex) {
			
			ex.printStackTrace();
			
			resultOperation = false;
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add(obj.getId().toString());
			listLog.add(log.language("ERROR_FIND_PK"));
			listLog.add(log.language("ERROR_CLASS") + ex.getClass().getName());
			log.recordLog(listLog);				
			
			throw new DataException(ErrorDao.FIND_PK, 
					language.getString("ERROR_FIND_PK") + "\n"  + 
					language.getString(obj.getClass().getSimpleName()) + " ;", 
					ex.getClass().getName(),
					language,
					environment);							
		}
		
		return new_obj;
	}
	
	/**
	 * Find object by field unique description. 
	 * {Pre: Field description}.
	 * {Post: return object if exist}. 
	 */
	public Base findObjectDescription (Base obj) throws DataException{
		
		resultOperation = false;
		
		Base new_base = null;
		
		
		try {
								
			new_base = (Base)em.createQuery("select o from " + obj.getClass().getSimpleName() + " o where o.description like :pDescription")
						.setParameter("pDescription", obj.getDescription())
						.getSingleResult();
			
			resultOperation = true;
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add(null);
			listLog.add(log.language("FIND_DESCRIPTION"));
			listLog.add(obj.toString());
			log.recordLog(listLog);				
		
		}
		catch (NoResultException Nex){
			
			resultOperation = false;
			
			Nex.printStackTrace();
			
			return null;
		
		}
		
		catch(Exception ex) {
			
			resultOperation = false;
			
			ex.printStackTrace();
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add(null);
			listLog.add(log.language("ERROR_FIND_DESCRIPTION"));
			listLog.add(log.language("ERROR_CLASS") + ex.getClass().getName());
			log.recordLog(listLog);				
			
			throw new DataException(ErrorDao.FIND_DESCRIPTION, 
					language.getString("ERROR_FIND_DESCRIPTION") + "\n"  + 
					language.getString(obj.getClass().getSimpleName()), 
					ex.getClass().getName(),
					language,
					environment);				
		
		}
		
		return new_base;
		
	}
	
	/**
	 * Find objects by some criterion. 
	 * {Pre: Some criterion}.
	 * {Post: return List objects if exist}. 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	public List<Base> findObjects (Base obj) throws DataException{
		
		resultOperation = false;
		String className = obj.getClass().getSimpleName();		
		AnalyzerConsult c = new AnalyzerConsult();
		String whereClause = c.makeWhere(obj);								
		
		
		try{
			if (em.isOpen()){										
																								
				System.out.println("select " +  className + " from " + className + " " + className + " " + whereClause);				
				
				listLog.clear();
				listLog.add(log.language(obj.getClass().getSimpleName()));
				listLog.add(null);
				listLog.add(log.language("FIND_OBJECTES"));
				listLog.add("select " +  className + " from " + className + " " + className + " " + whereClause);
				log.recordLog(listLog);	
				listObjects = (List<Base>)em.createQuery("select " +  className + " from " + className + " " + className +  " " + whereClause)
							.getResultList();								
				
			}
			
		}catch(Exception ex) {
			
			ex.printStackTrace();
			
			listLog.clear();
			listLog.add(log.language(obj.getClass().getSimpleName()));
			listLog.add(null);
			listLog.add(log.language("ERROR_FIND_OBJECTES"));
			listLog.add(log.language("ERROR_CLASS") + ex.getClass().getName());
			log.recordLog(listLog);				
			
			throw new DataException(ErrorDao.FIND_OBJECTS, 
					language.getString("ERROR_FIND_OBJECTES") + "\n"  + 
					language.getString(obj.getClass().getSimpleName()), 
					ex.getClass().getName(),
					language,
					environment);		
		
		}
				
		if (listObjects.size() > 0) resultOperation = true;
		
		return listObjects;
	}		
	
	
	/**
	  * Procedure that starts the transaction
	  * {Pre: There is an open connection to the database}.
	  * {Post: has initiated the transaction}.
	  * @throws DataException if there isn't a connection to the database.
	  * @throws DataException if there is an error to start the transaction.
	  */
	  public void begin()throws DataException {
	    
		
		try {
						
			em.getTransaction().begin();
			
			listLog.clear();
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("TRANSACTION_BEGIN"));
			log.recordLog(listLog);			
			
		} catch (Exception ex) {
			
			ex.printStackTrace();
			
			listLog.clear();
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("ERROR_TRANSACTION_BEGIN"));
			log.recordLog(listLog);
			
			throw new DataException(ErrorDao.TRANSACTION, ex.getMessage(),
					ex.getClass().getName(),
					language,
					environment);
		}
		
		return;
	  }	
	
	  /**
		* Result of the operation. 
		*/
	  public boolean isResultOperation() {
		
		  return resultOperation;
	  
	  }	
	  
	  
	  /**
		* Procedure that end the transaction accepting updates.
		* {Pre: There is an open connection to the database}.
		* {Post: End the transaction}.
		* @throws DataException if there isn't a connection to the database.
		* @throws DataException if there is an error to end the transaction.
		*/
	  public void commit()throws DataException {
		
		
		try{  
				
			if ( ! factory.isOpen() && ! em.isOpen()); 				
				
		} catch (Exception ex){
			
			ex.printStackTrace();
			
			listLog.clear();  
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("ERROR_CONNECTION_COMMIT"));
			log.recordLog(listLog);
			throw new DataException(ErrorDao.TRANSACTION, ex.getMessage(),
					ex.getLocalizedMessage() + ex.getCause(),
					language,
					environment);
		}
		  
		if ( ! em.getTransaction().isActive() )
			  
			return;
		  
		try {
			
			listLog.clear();  
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("TRANSACTION_END"));
			log.recordLog(listLog);			
			em.getTransaction().commit();						  	
		
		}catch (Exception ex) {
			
			ex.printStackTrace();
			
			System.out.println("Error " + ex.getLocalizedMessage() + ex.getCause().getClass());
			
			listLog.clear();  
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("ERROR_TRANSACTION_COMMIT"));
			log.recordLog(listLog);			
			resultOperation = false;			
			
			throw new DataException(ErrorDao.COMMIT, 
					language.getString("ERROR_TRANSACTION"),					
					ex.getCause().getClass().getName(),
					language,
					environment);
			
			
		}
		  
		return;
	  }
	  

	  /**
		* Procedure that end the transaction not accepting updates.
		* {Pre: There is an open connection to the database}.
		* {Post: End the transaction}.
		* @throws DataException if there isn't a connection to the database.
		* @throws DataException if there is an error to end the transaction.
		*/
	  public void rollback()throws DataException {
		 
		 
		 try{  
				
			  if ( ! factory.isOpen() && ! em.isOpen()); 
				
		} catch (Exception ex){
			
			ex.printStackTrace();
				
			listLog.clear();
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("ERROR_CONNECTION_ROLLBACK"));
			log.recordLog(listLog);	
				
			throw new DataException(ErrorDao.CONNETION, ex.getMessage(),
					ex.getClass().getName(),
					language,
					environment);
		}

		if ( ! em.getTransaction().isActive() )
		  return;
		
		try {
			
			em.getTransaction().rollback();
			listLog.clear();
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("TRANSACTION_ROLLBACK"));
			log.recordLog(listLog);				
			
		} catch (Exception ex) {
			
			ex.printStackTrace();
			
			listLog.clear();
			listLog.add(null);
			listLog.add(null);
			listLog.add(null);
			listLog.add(log.language("ERROR_TRANSACTION_ROLLBACK"));			
			resultOperation = false;
			throw new DataException(ErrorDao.TRANSACTION, ex.getMessage(),
					ex.getClass().getName(),
					language,
					environment);
		
		}
		return;
	  }
	  
	  /**
		* Procedure that closed the connection to database. 
		* {Pre: there isn't more references to object in memory}.
		* {Post: Closed the connection to database}. 
		*/
	  public void finalize() throws DataException {								
		
		em.close();
			
		factory.close();				
		
	  }	  	  	
	
	  
	/**Getters and setters*/  
	public User getUser() {
		
		return user;
	
	}

	public void setUser(User pUser){
		
		this.user = pUser;
		
		if (pUser.getLanguage() != null){
			
			language = ResourceBundle.getBundle("messagesDao_" + user.getLanguage());
			
		}
	}

	public ResourceBundle getLanguage() {
		
		return language;
	
	}
	
	public TypeEnvironment getEnvironment(){
		
		return environment;
	}
	
	public void setEnvironment(TypeEnvironment pEnvironment){
		
		environment= pEnvironment;
	}
	
	/**Instance associated with a persistence context*/
	public EntityManager getEntityManager(){
		
		return em;
	}
		
}
